package com.icehand.wechat.app;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.icehand.common.constants.CommonConstants;
import com.icehand.wechat.code.WechatErrorCode;
import com.icehand.wechat.exception.WechatException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.client.RestTemplate;

import java.util.Objects;

/**
 * @author icehand
 */
@Slf4j
@CacheConfig(cacheNames = "wechat-app")
public class WechatAppHelper {
    private static final String GET_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
    private static final String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s";
    private static final String GET_USER_INFO_URL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s";

    private final RestTemplate restTemplate;

    public WechatAppHelper(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    /**
     * wechat:openId:{appId}:{uid} -> openId
     * @param appId 微信appId
     * @param secret 微信secret
     * @param uid 用户编号
     * @param code 微信code
     * @return String
     */
    @Cacheable(key = "'openid:'+#appId+':'+#uid",condition = "#uid !=null")
    public String getOpenId(String appId, String secret, Long uid, String code){
        Objects.requireNonNull(appId,"appId can not be null");
        Objects.requireNonNull(secret,"secret can not be null");
        Objects.requireNonNull(code,"code can not be null");
        WechatAccessToken accessToken = code2AccessToken(appId,secret,uid,code);
        return accessToken.getOpenId();
    }

    /**
     *  wechat:token:{appId}:{uid} -> WechatAccessToken
     * 通过code获取accessToken，code仅可使用一次
     */
    @Cacheable(key = "'token:'+#appId+':'+#uid",condition = "#uid !=null")
    public WechatAccessToken code2AccessToken(String appId, String secret, Long uid, String code){
        Objects.requireNonNull(appId,"appId can not be null");
        Objects.requireNonNull(secret,"secret can not be null");
        Objects.requireNonNull(code,"code can not be null");
        String result = restTemplate.getForObject(String.format(GET_TOKEN_URL,appId,secret,code),String.class);
        return handAccessToken(result);
    }

    private WechatAccessToken handAccessToken(String body){
        if(Strings.isNullOrEmpty(body)){
            throw new WechatException("request access token fail");
        }

        JSONObject jsonObject = JSONObject.parseObject(body);
        if(jsonObject.containsKey(CommonConstants.K_ERR_CODE)){
            throw new WechatException(WechatErrorCode.choice(jsonObject.getInteger(CommonConstants.K_ERR_CODE)).getMsg());
        }
        return jsonObject.toJavaObject(WechatAccessToken.class);
    }

    /**
     * 刷新token
     */
    @CachePut(key = "'token:'+#appId+':'+#uid",condition = "#uid !=null")
    public WechatAccessToken refreshAccessToken(String appId, Long uid, String refreshToken) {
        Objects.requireNonNull(appId,"appId can not be null");
        Objects.requireNonNull(refreshToken,"refreshToken can not be null");
        String result = restTemplate.getForObject(String.format(REFRESH_TOKEN_URL,appId,refreshToken),String.class);
        return handAccessToken(result);
    }

    /**
     * wechat:user:{appId}:{uid} -> WechatAppUserInfo
     * 通过accessToken获取用户信息
     */
    @Cacheable(key = "'user:'+#appId+':'+#uid",condition = "#uid !=null")
    public WechatAppUserInfo accessToken2UserInfo(String appId, Long uid, String accessToken, String openId){
        Objects.requireNonNull(appId,"appId can not be null");
        Objects.requireNonNull(accessToken,"accessToken can not be null");
        Objects.requireNonNull(openId,"openId can not be null");
        String result = restTemplate.getForObject(String.format(GET_USER_INFO_URL,accessToken,openId),String.class);
        if(Strings.isNullOrEmpty(result)){
            throw new WechatException("accessToken 2 user info request fail");
        }

        JSONObject jsonObject = JSONObject.parseObject(result);
        Integer errCode = jsonObject.getInteger(CommonConstants.K_ERR_CODE);
        if(null != errCode) {
            throw new WechatException(WechatErrorCode.choice(errCode).getMsg());
        }

        return jsonObject.toJavaObject(WechatAppUserInfo.class);
    }
}
